﻿using System;
using System.ComponentModel;
using System.Net;
using System.Text;
using System.Threading;
using Netbits.Common.Conditions;
using Netbits.Common.General;

namespace Netbits.Common.Databases.MemoryTree
{
	/// <summary>
	/// Represent a memory-tree-field for based memory-tree-node
	/// </summary>
	public sealed class MemoryTreeField : IDisposable
	{
		#region Private Variables

		string _Name;
		int _Type;
		object _Value;

		#endregion

		#region Constructor

		internal MemoryTreeField( string Name_ )
		{
			Condition.Requires( Name_, "Name_" ).IsNotNull().IsNotEmpty();

			_Name = Name_;
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Returns the field name
		/// </summary>
		public string Name
		{
			get
			{
				return _Name;
			}
		}

		/// <summary>
		/// Returns the memory-tree-field data-type
		/// </summary>
		public MemoryTreeFieldType Type
		{
			get
			{
				return (MemoryTreeFieldType)_Type;
			}
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Clones this memory-tree-field to specified memory-tree-node
		/// </summary>
		/// <returns>the new clone of this memory-tree-field</returns>
		public MemoryTreeField Clone()
		{
			MemoryTreeField mtf = new MemoryTreeField( _Name );
			mtf._Type = _Type;
			mtf._Value = _Value;
			return mtf;
		}

		/// <summary>
		/// Returns the the typed value. 
		/// </summary>
		/// <typeparam name="TValue">type of this value to return</typeparam>
		/// <returns>Return the typed value otherwise any exception</returns>
		public TValue GetValue<TValue>()
		{
			Type t = typeof( TValue );
			switch( (MemoryTreeFieldType)_Type )
			{
				case MemoryTreeFieldType.Bool:
					if( t != typeof( bool ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.Byte:
					if( t != typeof( sbyte ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.DateTime:
					if( t != typeof( DateTime ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.Decimal:
					if( t != typeof( decimal ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.Double:
					if( t != typeof( double ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.Float:
					if( t != typeof( float ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.Int:
					if( t != typeof( int ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.IPAddress:
					if( t != typeof( IPAddress ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.Long:
					if( t != typeof( long ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.Short:
					if( t != typeof( short ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.String:
					if( t != typeof( string ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.Timespan:
					if( t != typeof( TimeSpan ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.UByte:
					if( t != typeof( byte ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.UInt:
					if( t != typeof( uint  ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.ULong:
					if( t != typeof( ulong ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.UShort:
					if( t != typeof( ushort ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.Guid:
					if( t != typeof( Guid ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				case MemoryTreeFieldType.Version:
					if( t != typeof( Version ) )
						throw new InvalidCastException();
					return (TValue)_Value;
				default:
					throw new InvalidCastException();
			}
		}

		/// <summary>
		/// Set the value of this memory-tree-field.
		/// The type of the field was auto detected.
		/// </summary>
		/// <param name="Value_">The new value for this memory-tree-field</param>
		public void SetValue( object Value_ )
		{
			Condition.Requires( Value_, "Value_" ).IsNotNull();

			Type t = Value_.GetType();
			int type = (int)MemoryTreeFieldType.Unknown;
			if( t == typeof( sbyte ) )
				type = (int)MemoryTreeFieldType.Byte;
			else if( t == typeof( bool ) )
				type = (int)MemoryTreeFieldType.Bool;
			else if( t == typeof( DateTime ) )
				type = (int)MemoryTreeFieldType.DateTime;
			else if( t == typeof( decimal ) )
				type = (int)MemoryTreeFieldType.Decimal;
			else if( t == typeof( double ) )
				type = (int)MemoryTreeFieldType.Double;
			else if( t == typeof( float ) )
				type = (int)MemoryTreeFieldType.Float;
			else if( t == typeof( int ) )
				type = (int)MemoryTreeFieldType.Int;
			else if( t == typeof( IPAddress ) )
				type = (int)MemoryTreeFieldType.IPAddress;
			else if( t == typeof( long ) )
				type = (int)MemoryTreeFieldType.Long;
			else if( t == typeof( short ) )
				type = (int)MemoryTreeFieldType.Short;
			else if( t == typeof( string ) )
				type = (int)MemoryTreeFieldType.String;
			else if( t == typeof( TimeSpan ) )
				type = (int)MemoryTreeFieldType.Timespan;
			else if( t == typeof( byte ) )
				type = (int)MemoryTreeFieldType.UByte;
			else if( t == typeof( uint ) )
				type = (int)MemoryTreeFieldType.UInt;
			else if( t == typeof( ulong ) )
				type = (int)MemoryTreeFieldType.ULong;
			else if( t == typeof( ushort ) )
				type = (int)MemoryTreeFieldType.UShort;
			else if( t == typeof( Guid ) )
				type = (int)MemoryTreeFieldType.Guid;
			else if( t == typeof( Version ) )
				type = (int)MemoryTreeFieldType.Version;
			else
				throw new InvalidCastException( "Unknown value-type" );

			Interlocked.Exchange( ref _Type, type );
			Interlocked.Exchange( ref _Value, Value_ );
		}

		#endregion

		#region Internal Functions

		/// <summary>
		/// Read from string-text (parsing)
		/// </summary>
		/// 
		internal int FromText( string Text_, int Index_ )
		{
			byte state = 0;
			int stringSize = 0;
			int index = Index_;
			bool startSet = false;
			bool typeSet = false;
			string value = string.Empty;
			StringBuilder sb = new StringBuilder();
			int maxLength = Text_.Length;

			while( index < maxLength )
			{
				char c = Text_[index++];
				switch( c )
				{
					case '{':
						startSet = true;
						continue;
					case '}':
						if( startSet == false )
							throw new Exception( "invalid field state. start not set" );

						if( typeSet == false )
							throw new Exception( "invalid field state. type not set" );

						if( string.IsNullOrEmpty( value ) )
							value = sb.ToString();
						sb.Length = 0;

						switch( (MemoryTreeFieldType)_Type )
						{
							case MemoryTreeFieldType.Bool:
								{
									bool v;
									if( !bool.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct boolean format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.Byte:
								{
									sbyte v;
									if( !sbyte.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct sbyte format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.DateTime:
								{
									long v;
									if( !long.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct datetime format" );
									_Value = new DateTime( v );
								}
								break;
							case MemoryTreeFieldType.Decimal:
								{
									decimal v;
									if( !decimal.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct decimal format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.Double:
								{
									double v;
									if( !double.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct double format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.Float:
								{
									float v;
									if( !float.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct float format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.Int:
								{
									int v;
									if( !int.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct int format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.IPAddress:
								{
									IPAddress ip;
									if( !IPAddress.TryParse( value, out ip ) )
										throw new Exception( "invalid field value. can't parse value to correct ip-address format" );
									_Value = ip;
								}
								break;
							case MemoryTreeFieldType.Long:
								{
									long v;
									if( !long.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct long format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.Short:
								{
									short v;
									if( !short.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct short format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.String:
								_Value = value;
								break;
							case MemoryTreeFieldType.Timespan:
								{
									long v;
									if( !long.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct Timespan format" );
									_Value = new TimeSpan( v );
								}
								break;
							case MemoryTreeFieldType.UByte:
								{
									byte v;
									if( !byte.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct ubyte format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.UInt:
								{
									uint v;
									if( !uint.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct uint format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.ULong:
								{
									ulong v;
									if( !ulong.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct ulong format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.UShort:
								{
									ushort v;
									if( !ushort.TryParse( value, out v ) )
										throw new Exception( "invalid field value. can't parse value to correct ushort format" );
									_Value = v;
								}
								break;
							case MemoryTreeFieldType.Guid:
								_Value = Converter.ToGuid( value );
								break;
							case MemoryTreeFieldType.Version:
								_Value = Converter.ToVersion( value );
								break;
						}
						return index;
					case ':':
						if( startSet == false )
							throw new Exception( "invalid field state. separator before start-tag at position" );

						// Field: {NAME:TYPE:SIZE:VALUE}
						// Field: {NAME:TYPE:VALUE}

						switch( state )
						{
							case 0:
								_Name = sb.ToString();
								break;
							case 1:
								_Type = Converter.ConvertTo<int>( sb.ToString(), (int)MemoryTreeFieldType.Unknown );
								if( _Type == (int)MemoryTreeFieldType.Unknown )
									throw new Exception( "invalid field state. can't handle invalid field-type" );
								typeSet = true;
								break;
							case 2:
								stringSize = Converter.ConvertTo<int>( sb.ToString() );
								if( stringSize < 0 )
									throw new Exception( "invalid field state. string-size <= 0" );

								value = Text_.Substring( index, stringSize );
								index += stringSize;
								break;
							default:
								throw new Exception( "invalid field state > 2" );
						}
						sb.Length = 0;
						++state;
						break;
					default:
						if( startSet == false )
							throw new Exception( "invalid field state. separator before start-tag" );
						sb.Append( c );
						break;
				}
			}

			throw new Exception( "stream EOF. field not finished!" );
		}

		/// <summary>
		/// Create the representative string for this field and write it to the string-builder.
		/// </summary>
		/// <param name="Builder_">StringBuilder instance to write the string</param>
		internal void ToStringBuilder( StringBuilder Builder_ )
		{
			Builder_.Append( '{' );
			Builder_.Append( _Name );
			Builder_.Append( ':' );
			Builder_.Append( _Type );
			Builder_.Append( ':' );

			switch( (MemoryTreeFieldType)_Type )
			{
				case MemoryTreeFieldType.DateTime:
					Builder_.Append( ((DateTime)_Value).Ticks );
					break;
				case MemoryTreeFieldType.Timespan:
					Builder_.Append( ((TimeSpan)_Value).Ticks );
					break;
				case MemoryTreeFieldType.String:
				case MemoryTreeFieldType.Version:
					{
						string tmpval = _Value.ToString();
						Builder_.Append( string.Format( "{0}:{1}", tmpval.Length, tmpval ) );
					}
					break;
				default:
					Builder_.Append( _Value.ToString() );
					break;
			}
			Builder_.Append( '}' );
		}

		#endregion

		#region Overrides

		/// <summary>
		/// Returns the representative string for the value
		/// </summary>
		/// <returns>The representative string</returns>
		public override string ToString()
		{
			string value;
			switch( _Type )
			{
				case (int)MemoryTreeFieldType.DateTime:
					value = ((DateTime)_Value).Ticks.ToString();
					break;
				case (int)MemoryTreeFieldType.Timespan:
					value = ((TimeSpan)_Value).Ticks.ToString();
					break;
				default:
					value = _Value.ToString();
					break;
			}
			return value;
		}

		#endregion

		#region IDisposeable

		/// <summary>
		/// Reset all data. 
		/// </summary>
		public void Dispose()
		{
			_Name = string.Empty;
			_Value = null;
		}

		#endregion
	}
}
